/*
 * @(#)TypeFactory.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.lang;

import java.util.Random;

/**
 * Clase que describe un tipo de variable linguistica
 * 
 * @author Francisco Jose Moreno Velo
 *
 */
public class TypeFactory implements Cloneable {

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                              //
	//----------------------------------------------------------------------------//


	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//


 	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Crea un conjunto de triangulos equiespaciados
	 */
	public static void createFreeTriangles(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(mfs-1);
		double param[] = new double[3];
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		param[0] = min - r;
		param[1] = min;
		param[2] = min + r;
		pmf[0] = new xfuzzy.pkg.xfl.mfunc.triangle();
		pmf[0].set("mf0",u);
		try { pmf[0].set(param); }
		catch(XflException ex) { System.out.println(""+ex); }

		for(int i=1; i<mfs-1; i++) {
			param[0] = min + (i-1)*r;
			param[1] = min + i*r;
			param[2] = min + (i+1)*r;
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.triangle();
			pmf[i].set("mf"+i,u);
			try { pmf[i].set(param); } catch(XflException ex) {}
		}

		param[0] = max - r;
		param[1] = max;
		param[2] = max + r;
		pmf[mfs-1] = new xfuzzy.pkg.xfl.mfunc.triangle();
		pmf[mfs-1].set("mf"+(mfs-1),u);
		try { pmf[mfs-1].set(param); } catch(XflException ex) {}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de triangulos y trapecios, de forma que los
	 * intervalos de pertenencia mayor que 0.5 sean iguales en todos ellos
	 */
	public static void createFreeShoulderedTriangles(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(2*mfs);
		double param4[] = new double[4];
		double param3[] = new double[3];
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		param4[0] = min-2*r;
		param4[1] = min;
		param4[2] = min+r;
		param4[3] = min+3*r;
		pmf[0] = new xfuzzy.pkg.xfl.mfunc.trapezoid();
		pmf[0].set("mf0",u);
		try { pmf[0].set(param4); } catch(XflException ex) {}

		for(int i=1; i<mfs-1; i++) {
			param3[0] = min + (2*i-1)*r;
			param3[1] = min + (2*i+1)*r;
			param3[2] = min + (2*i+3)*r;
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.triangle();
			pmf[i].set("mf"+i,u);
			try { pmf[i].set(param3); } catch(XflException ex) {}
		}

		param4[0] = max-3*r;
		param4[1] = max-r;
		param4[2] = max;
		param4[3] = max+2*r;
		pmf[mfs-1] = new xfuzzy.pkg.xfl.mfunc.trapezoid();
		pmf[mfs-1].set("mf"+(mfs-1), u);
		try { pmf[mfs-1].set(param4); } catch(XflException ex) {}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de campanas equiespaciadas
	 */
	public static void createFreeGaussians(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(mfs-1);
		double param[] = new double[2];
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		param[0] = min;
		param[1] = 0.6*r;
		pmf[0] = new xfuzzy.pkg.xfl.mfunc.bell();
		pmf[0].set("mf0", u);
		try { pmf[0].set(param); } catch(XflException ex) {}

		for(int i=1; i<mfs-1; i++) {
			param[0] = min + i*r;
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.bell();
			pmf[i].set("mf"+i, u);
			try { pmf[i].set(param); } catch(XflException ex) {}
		}

		param[0] = max;
		pmf[mfs-1] = new xfuzzy.pkg.xfl.mfunc.bell();
		pmf[mfs-1].set("mf"+(mfs-1), u);
		try { pmf[mfs-1].set(param); } catch(XflException ex) {}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de campanas y sigmoides
	 */
	public static void createFreeShoulderedBells(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(mfs+1);
		double param[] = new double[2];
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		param[0] = min + 1.5*r;
		param[1] = -0.17*r;
		pmf[0] = new xfuzzy.pkg.xfl.mfunc.sigma();
		pmf[0].set("mf0", u);
		try { pmf[0].set(param); } catch(XflException ex) {}

		for(int i=1; i<mfs-1; i++) {
			param[0] = min + (i+1)*r;
			param[1] = 0.6*r;
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.bell();
			pmf[i].set("mf"+i, u);
			try { pmf[i].set(param); } catch(XflException ex) {}
		}

		param[0] = max - 1.5*r;
		param[1] = 0.17*r;
		pmf[mfs-1] = new xfuzzy.pkg.xfl.mfunc.sigma();
		pmf[mfs-1].set("mf"+(mfs-1), u);
		try { pmf[mfs-1].set(param); } catch(XflException ex) {}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}
	
	/**
	 * Crea un conjunto de trapecios equiespaciados
	 */
	public static void createFreeTrapezoids(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(3*mfs-1);
		double param[] = new double[4];
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		param[0] = min - r;
		param[1] = min;
		param[2] = min + 2*r;
		param[3] = min + 3*r;
		pmf[0] = new xfuzzy.pkg.xfl.mfunc.trapezoid();
		pmf[0].set("mf0", u);
		try { pmf[0].set(param); } catch(XflException ex) {}

		for(int i=1; i<mfs-1; i++) {
			param[0] = min + (3*i-1)*r;
			param[1] = min + 3*i*r;
			param[2] = min + (3*i+2)*r;
			param[3] = min + (3*i+3)*r;
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.trapezoid();
			pmf[i].set("mf"+i, u);
			try { pmf[i].set(param); } catch(XflException ex) {}
		}

		param[0] = max - 3*r;
		param[1] = max - 2*r;
		param[2] = max;
		param[3] = max + r;
		pmf[mfs-1] = new xfuzzy.pkg.xfl.mfunc.trapezoid();
		pmf[mfs-1].set("mf"+(mfs-1), u);
		try { pmf[mfs-1].set(param); } catch(XflException ex) {}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de singularidades equiespaciadas
	 */
	public static void createClasses(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(mfs-1);
		double param;
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		param = min;
		pmf[0] = new xfuzzy.pkg.xfl.mfunc.singleton();
		pmf[0].set("mf0", u);
		pmf[0].set(param);

		for(int i=1; i<mfs-1; i++) {
			param = min + i*r;
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.singleton();
			pmf[i].set("mf"+i, u);
			pmf[i].set(param);
		}

		param = max;
		pmf[mfs-1] = new xfuzzy.pkg.xfl.mfunc.singleton();
		pmf[mfs-1].set("mf"+(mfs-1), u);
		pmf[mfs-1].set(param);

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de singularidades equiespaciadas
	 */
	public static void createClasses(Type type, String[] label) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(label.length-1);
		double param;
		int mfs = label.length;
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		for(int i=0; i<mfs-1; i++) {
		param = min + i*r;
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.singleton();
			pmf[i].set(label[i].replace('-', '_'), u);
			pmf[i].set(param);
		}

		param = max;
		pmf[mfs-1] = new xfuzzy.pkg.xfl.mfunc.singleton();
		pmf[mfs-1].set(label[mfs-1].replace('-', '_'), u);
		pmf[mfs-1].set(param);

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}
	
	/**
	 * Crea un conjunto de campanas centradas
	 */
	public static void createCenteredBells(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double param[] = new double[2];
		param[0] = (min + max)/2;
		param[1] = (max - min)/8;
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		for(int i=0; i<mfs; i++) {
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.bell();
			pmf[i].set("mf"+i, u);
			try { pmf[i].set(param); } catch(XflException ex) {}
		}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de singularidades centradas
	 */
	public static void createCenteredSingletons(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double middle = (min + max)/2 ;
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		for(int i=0; i<mfs; i++) {
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.singleton();
			pmf[i].set("mf"+i, u);
			pmf[i].set(middle);
		}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de campanas colocadas aleatoriamente
	 */
	public static void createRandomBells(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		Random random = new Random();
		double param[] = new double[2];
		param[1] = (max - min)/8;
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		for(int i=0; i<mfs; i++) {
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.bell();
			pmf[i].set("mf"+i, u);
			param[0] = u.point(random.nextDouble());
			try { pmf[i].set(param); } catch(XflException ex) {}
		}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}

	/**
	 * Crea un conjunto de singularidades aleatorias
	 */
	public static void createRandomSingletons(Type type, int mfs) {
		Universe u = type.getUniverse();
		Random random = new Random();
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		for(int i=0; i<mfs; i++) {
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.singleton();
			pmf[i].set("mf"+i, u);
			pmf[i].set(u.point(random.nextDouble()));
		}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}
	
	/**
	 * Crea un conjunto de funciones de Takagi-Sugeno
	 */
	public static void createTakagi(Type type, int mfs, int np) {
		Universe u = type.getUniverse();
		double param[] = new double[np];
		for(int i=0; i<np; i++) param[i] = 1.0;
		ParamMemFunc pmf[] = new ParamMemFunc[mfs];

		for(int i=0; i<mfs; i++) {
			pmf[i] = new xfuzzy.pkg.xfl.mfunc.parametric();
			pmf[i].set("mf"+i, u);
			try { pmf[i].set(param); } catch(XflException ex) {}
		}

		try { for(int i=0; i<mfs; i++) type.add(pmf[i]); }
		catch(XflException e) {}
	}
	
	/**
	 * Crea una familia de triangulos equiespaciados
	 */
	public static void createTriangularFamily(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(mfs-1);
		double param[] = new double[mfs-2];
		for(int i=0; i<param.length; i++) param[i] = min + (i+1)*r;

		Family fam = new xfuzzy.pkg.xfl.family.triangular();
		fam.set("fam",type);
		try { fam.set(param); type.addFamily(fam); } catch(XflException ex) {}

		for(int i=0; i<mfs; i++) {
			FamiliarMemFunc fmf = new FamiliarMemFunc("mf"+i,fam,i);
			try { type.add(fmf); } catch(XflException e) {}
		}
	}

	/**
	 * Crea una familia de triangulos y trapecios
	 */
	public static void createShoulderedTriangularFamily(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(mfs+1);
		double param[] = new double[mfs];
		for(int i=0; i<param.length; i++) param[i] = min + (i+1)*r;

		Family fam = new xfuzzy.pkg.xfl.family.sh_triangular();
		fam.set("fam",type);
		try { fam.set(param); type.addFamily(fam); } catch(XflException ex) {}

		for(int i=0; i<mfs; i++) {
			FamiliarMemFunc fmf = new FamiliarMemFunc("mf"+i,fam,i);
			try { type.add(fmf); } catch(XflException e) {}
		}
	}

	/**
	 * Crea una familia de splines equiespaciados
	 */
	public static void createBsplinesFamily(Type type, int mfs) {
		Universe u = type.getUniverse();
		double min = u.min();
		double max = u.max();
		double r = (max-min)/(mfs-2);
		double param[] = new double[mfs-3];
		for(int i=0; i<mfs-3; i++) param[i] = min+(i+1)*r;

		String famname = "fam";
		int index = 0;
		while(type.searchFamily(famname) != null) famname = "fam"+index;

		Family fam = new xfuzzy.pkg.xfl.family.spline();
		fam.set(famname,type);
		try { 
			fam.set(param);
			type.addFamily(fam);
		} catch(Exception ex) {}

		for(int i=0; i<mfs; i++) {
			FamiliarMemFunc fmf = new FamiliarMemFunc("mf"+i,fam,i);
			try { type.add(fmf); } catch(XflException e) {}
		}
	}

}
